// Own includes
#include "kickass.h"
#include "kickass_config.h"
 
// Include the basics
#include <ktexteditor/document.h>
#include <kmessagebox.h>
#include <kpassivepopup.h>
#include <kpluginfactory.h>
#include <kpluginloader.h>
#include <klocale.h>
#include <kaction.h>
#include <kprocess.h>
#include <qstring.h>
#include <kactioncollection.h>
#include <kdatetime.h>
#include <kparts/part.h>
#include <kconfiggroup.h>

#include <iostream>
// This macro defines a KPluginFactory subclass named KickassPluginFactory. The second
// argument to the macro is code that is inserted into the constructor of the class.
// I our case all we need to do is register one plugin. If you want to have more
// than one plugin in the same library then you can register multiple plugin classes
// here. The registerPlugin function takes an optional QString parameter which is a
// keyword to uniquely identify the plugin then (it maps to X-KDE-PluginKeyword in the
// .desktop file).
/*
K_PLUGIN_FACTORY(KickassPluginFactory,
                 registerPlugin<KickassPlugin>();
                )*/



KickassPlugin *KickassPlugin::plugin = 0;

K_PLUGIN_FACTORY_DEFINITION(KickassPluginFactory,
        registerPlugin<KickassPlugin>("ktexteditor_kickass");
        registerPlugin<KickassConfig>("ktexteditor_kickass_config");
        )

// With the next macro call, the library exports version information about the
// Qt and KDE libraries being used and (most important) the entry symbol to get at
// the factory we defined above.
// The argument this macro takes is the constructor call of the above factory which
// provides two constructors. One which takes a KAboutData* and another one
// that takes two (optional) const char* parameters (Same as for KComponentData
// constructors).
// We put there the X-KDE-LibraryName.
// Is important to provide as last parameter "ktexteditor_plugins".
K_EXPORT_PLUGIN(KickassPluginFactory("ktexteditor_kickass", "ktexteditor_plugins"))
 
// Constructor
KickassPlugin::KickassPlugin(QObject *parent, const QVariantList &args)
    : KTextEditor::Plugin(parent), m_autoBrackets(true), m_autoQuotations(true)
{
    // Avoid warning on compile time because of unused argument
    Q_UNUSED(args);

    plugin = this;
    readConfig();
    
}
 
// Destructor
KickassPlugin::~KickassPlugin()
{
    plugin = 0;
}
 
// Create the plugin view class and add it to the views list
void KickassPlugin::addView(KTextEditor::View *view)
{
    KickassPluginView *nview = new KickassPluginView(m_path,m_kOptions,m_vOptions,m_tOptions,m_terminal,view);
    m_views.append(nview);
}
 
// Find the view where we want to remove the plugin from, and remove it.
// Do not forget to free the memory.
void KickassPlugin::removeView(KTextEditor::View *view)
{
    for (int z = 0; z < m_views.size(); z++)
    {
        if (m_views.at(z)->parentClient() == view)
        {
            KickassPluginView *nview = m_views.at(z);
            m_views.removeAll(nview);
            delete nview;
        }
    }
}
 

void KickassPlugin::readConfig()
{
    KConfigGroup cg(KGlobal::config(), "Kickass Plugin");
    // Read configuration parameters, make them false by default
    m_path = cg.readEntry("strPath", pathKickass);
    m_kOptions = cg.readEntry("strkOptions", gkOptions);
    m_vOptions = cg.readEntry("strvOptions", gvOptions);
    m_tOptions = cg.readEntry("strtOptions", gtOptions);
    m_terminal = cg.readEntry("terminal",gTerminal);
}
 
void KickassPlugin::writeConfig()
{
    KConfigGroup cg(KGlobal::config(), "Kickass Plugin");
    cg.writeEntry("strPath", m_path );
    cg.writeEntry("strkOptions", m_kOptions);
    cg.writeEntry("strvOptions", m_vOptions);
    cg.writeEntry("strtOptions", m_tOptions);
    cg.writeEntry("terminal", m_terminal);
}

void KickassPlugin::setPath(const QString &path)
{
	m_path=path;
	foreach(KickassPluginView *pluginview,m_views)
		pluginview->setPath(path);
}


void KickassPlugin::setkOptions(const QString &opts)
{
	m_kOptions=opts;
	foreach(KickassPluginView *pluginview,m_views)
		pluginview->setkOptions(opts);
}

void KickassPlugin::setvOptions(const QString &opts)
{
	m_vOptions=opts;
	foreach(KickassPluginView *pluginview,m_views)
		pluginview->setvOptions(opts);
}

void KickassPlugin::settOptions(const QString &opts)
{
	m_tOptions=opts;
	foreach(KickassPluginView *pluginview,m_views)
		pluginview->settOptions(opts);
}

void KickassPlugin::setTerminal(const bool on)
{
	m_terminal=on;
	foreach(KickassPluginView *pluginview,m_views)
		pluginview->setTerminal(on);
}

 
// Plugin view class
KickassPluginView::KickassPluginView(QString path,QString kopts, QString vopts,QString topts,bool bterm,KTextEditor::View *view)
  : QObject(view)
  , KXMLGUIClient(view)
  , m_view(view)
  , m_path(path)
  , m_kOptions(kopts)
  , m_vOptions(vopts)
  , m_tOptions(topts)
  , m_terminal(bterm)
  , m_run(0)
{
    setComponentData(KickassPluginFactory::componentData());
 
    proc = new KProcess;
    proc->setOutputChannelMode(KProcess::MergedChannels);
//     KAction *action = new KAction(i18n("Assemble and run in Vice"), this);
//     // Here we need as first parameter the same we declared at the resource
//     // contents file (timedateui.rc). We named the action "tools_insert_timedate".
//     // Here is where we connect it to an actual KDE action.
//     actionCollection()->addAction("tools_kickass", action);
//     action->setShortcut(Qt::CTRL + Qt::Key_F1);
//     // As usual, we connect the signal triggered() to a slot here. When the menu
//     // element is clicked, we go to the slot slotAssembleNRun().
//     connect(action, SIGNAL(triggered()), this, SLOT(slotAssembleNRun()));
 
    KAction *a=actionCollection()->addAction("assemble");
    a->setText(i18n("Assemble"));
    connect(a, SIGNAL(triggered()), this, SLOT(slotAssembleNRun()));
    a->setShortcut(Qt::CTRL + Qt::Key_1);
    
    a=actionCollection()->addAction("assemblerun");
    a->setText(i18n("Assemble and run"));
    connect(a, SIGNAL(triggered()), this, SLOT(slotRun()));
    a->setShortcut(Qt::CTRL + Qt::Key_2);
    
    a=actionCollection()->addAction("showerror");
    a->setText(i18n("Display Error again"));
    connect(a, SIGNAL(triggered()), this, SLOT(slotError()));
    a->setShortcut(Qt::CTRL + Qt::Key_3);
    a->setEnabled(false);
    m_errAction=a;
    // This is always needed, tell the KDE XML GUI client that we are using
    // that file for reading actions from.
    setXMLFile("kickassui.rc");
}
 
// Destructor
KickassPluginView::~KickassPluginView()
{
	delete proc;
}


void KickassPluginView::slotError()
{
	if (m_errLine!=-1)
		m_view->setCursorPosition(KTextEditor::Cursor(m_errLine-1, 8));
	KPassivePopup::message(i18n("Assembling issues"),m_msgs,m_view);
}
void KickassPluginView::slotRun()
{
	m_run=1;
	slotAssembleNRun();
	m_run=0;
}
// The slot that will be called when the menu element "Insert Time & Date" is
// clicked.
void KickassPluginView::slotAssembleNRun()
{
    QString base;

    KTextEditor::Document *doc = m_view->document();
    if (doc) {
	
	doc->save();
	QString filename=doc->documentName();
	int dot=filename.indexOf(".");
	if (dot==-1 || (!filename.endsWith(".s",Qt::CaseInsensitive) &&
			!filename.endsWith(".asm",Qt::CaseInsensitive))) {
		KMessageBox::error(0, i18n("Sorry can't work with files without .s or .asm extension"));
		return;
	}
	
	KUrl dir(doc->url());
	dir = dir.upUrl();

	base=filename.left(dot);

	m_errAction->setEnabled(false);
	m_msgs="";
	m_errLine=-1;
	
	QString cmd="";
	QString expkOptions=m_kOptions;
	QString expvOptions=m_vOptions;
	QString exptOptions=m_tOptions;
	
	expkOptions=expkOptions.replace("%f",filename);
	expkOptions=expkOptions.replace("%b",base);
	expvOptions=expvOptions.replace("%f",filename);
	expvOptions=expvOptions.replace("%b",base);
	
	exptOptions=exptOptions.replace("%p",dir.path());
	exptOptions=exptOptions.replace("%e",expvOptions);
	
	//the following check should cater for not java assemblers (eg. Dasm)...totally untested :P
	if (m_path.endsWith(".jar",Qt::CaseInsensitive))
		cmd="java -jar "+m_path+" "+expkOptions;
	else
		cmd=m_path+" "+expkOptions;
		
	cmd+=" "+filename;
	
	//QApplication::connect(proc, SIGNAL(processExited(KProcess *)),pointer_to_my_object, SLOT(my_objects_slot(KProcess *)));
 	//std::cout << "-------------------------------" << qPrintable(dir.path()) << std::endl;
	
	proc->setWorkingDirectory(dir.path());
	//std::cout << "Calling assembler: \""<<qPrintable(cmd)<<"\""<<std::endl;
	proc->setShellCommand(cmd);
	proc->start();
	if(!proc->waitForStarted(500)) {
		KMessageBox::error(0, i18n("Failed to run \"%1\". exitStatus = %2", cmd, proc->exitStatus()));
		return;
        }
	bool ok=proc->waitForFinished(-1);
	if (ok) {
	    if (proc->exitCode()!=0) {
		m_errAction->setEnabled(true);
		QString output=QString::fromUtf8(proc->readAll());
		int s=output.indexOf("at line ");
		if (s!=-1) {
			s+=8;
			int e=output.indexOf(" ",s);
			if (e!=-1) {
				QString strline=output.mid(s,(e-s-1));
				int n=strline.toInt();
				m_errLine=n;
				 m_view->setCursorPosition(KTextEditor::Cursor(n-1, 8));
				 //std::cout << "--------------------- " << n << std::endl;
				 //std::cout << "--------------------- " << qPrintable(strline) << std::endl;
			}	
		}
		KPassivePopup::message(i18n("Assembling issues"),output,m_view);
		m_msgs=output;
	    } else
		KPassivePopup::message(i18n("Assembling success"),"All went well!",m_view);
	}
	//std::cout << "--------------------------------" << proc->exitCode() << std::endl;
	if (ok && proc->exitCode()==0 && m_run==1) {
		//cmd = "konsole -workdir "+dir.path() +" -e x64 "+expvOptions+" "+base+".prg";
		if (m_terminal)
			cmd=exptOptions;
		else
			cmd=expvOptions;
		
		//proc->setWorkingDirectory(dir.path());
		std::cout << "Calling emulator: \""<<qPrintable(cmd)<<"\""<<std::endl;
		proc->setShellCommand(cmd);
		proc->start();
		if(!proc->waitForStarted(500)) {
			KMessageBox::error(0, i18n("Failed to run \"%1\". exitStatus = %2", cmd, proc->exitStatus()));
			return;
		}
	}
	
        //kv->insertText (base);
    }
}
 
// We need to include the moc file since we have declared slots and we are using
// the Q_OBJECT macro on the KickassPluginView class.
#include "kickass.moc"